package com.hooper.wallet.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hooper.wallet.constant.TransactTypeEnum;
import com.hooper.wallet.domain.TransactRecord;
import com.hooper.wallet.domain.UserWallet;
import com.hooper.wallet.mapper.UserWalletMapper;
import com.hooper.wallet.service.TransactRecordService;
import com.hooper.wallet.service.UserWalletService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

import static com.hooper.wallet.util.ValidationCheckUtil.isIdNotValidate;

/**
 * @author Mind
 * @description 针对表【user(用户表)】的数据库操作Service实现
 * @createDate 2023-03-07 18:56:03
 */
@Slf4j
@Service
public class UserWalletServiceImpl extends ServiceImpl<UserWalletMapper, UserWallet>
        implements UserWalletService {

    @Resource
    private UserWalletMapper userWalletMapper;
    @Resource
    private TransactRecordService transactRecordService;
    @Resource
    private RedissonClient redissonClient;
    



    @Override
    public String checkBalance(Long userId) {
        if (isIdNotValidate(userId)) {
            throw new RuntimeException("id not validate!");
        }
        UserWallet userWallet = userWalletMapper.selectById(userId);
        if (userWallet == null) {
            throw new RuntimeException("user not exist!");
        }
        return String.format("user-%s: ur current balance is %d.",
                userWallet.getUsername(), userWallet.getBalance());
    }


    @Override
    public String costMoney(Long userId, Integer cost) {
        if (isIdNotValidate(userId)) {
            throw new RuntimeException("id not validate!");
        }
        //可能会有0元的订单
        if (cost < 0) {
            throw new RuntimeException("params not validate");
        }
        RLock lock = redissonClient.getLock(userId.toString());
        try {
            if (lock.tryLock()) {
                UserWallet userWallet = userWalletMapper.selectById(userId);
                if (userWallet == null) {
                    throw new RuntimeException("user not exists!");
                }
                Integer balance = userWallet.getBalance();
                if (balance < cost) {
                    throw new RuntimeException("balance not enough error");
                }
                //修改余额
                userWallet.setBalance(balance - cost);
                TransactRecord transactRecord = createTransactRecord(userId, cost,
                        TransactTypeEnum.CONSUME.getType());
                //将记录添加到交易记录表中
                transactRecordService.save(transactRecord);
                //更新余额
                userWalletMapper.updateById(userWallet);
                return String.format("user-%s successfully cost %d CNY", userWallet.getUsername(), cost);
            }
        } finally {
            lock.unlock();
        }
        //若未获得锁，则表明该用户正在进行其他修改操作。
        // 直接返回信息即可
        return "try again later";
    }
    @Override
    public String withdraw(Long userId, Integer amount) {
        if (isIdNotValidate(userId)) {
            throw new RuntimeException("user id not validate!");
        }
        if (amount <= 0) {
            throw new RuntimeException("withdraw amount should more than 0 !");
        }

        RLock lock = redissonClient.getLock(userId.toString());
        try {
            if (lock.tryLock()) {
                UserWallet userWallet = userWalletMapper.selectById(userId);
                if (userWallet == null) {
                    throw new RuntimeException("user not exists");
                }
                boolean callbackSuccess = bankRemoteCall(userId, amount);
                if (!callbackSuccess) {
                    throw new RuntimeException("transact error, please try it later!");
                }
                Integer balance = userWallet.getBalance();
                if (balance < amount){
                    throw new RuntimeException("balance not enough!");
                }
                //更新
                userWallet.setBalance(balance - amount);
                //新增记录
                TransactRecord transactRecord = createTransactRecord(userId, amount,
                        TransactTypeEnum.WITHDRAW.getType());
                transactRecordService.save(transactRecord);
                userWalletMapper.updateById(userWallet);
                return String.format("user-%s successfully withdraw %d.",
                        userWallet.getUsername(), amount);
            }
        } finally {
            lock.unlock();
        }
        return "try again later";
    }


     /*   TransactRecord record = transactRecordService.getById(recordId);
        用户需要由消费记录才能退款（不清楚具体业务只能这样理解）
        由于只需要100的消费接口和20的退款接口，因此注释方便测试
            if (record == null) {
                log.error("user refund error --> doesn't have any records. user id :{}", userId);
                throw new RuntimeException("no any records, can't refund!");
            }
            if (!Objects.equals(record.getTransactType(), TransactTypeEnum.CONSUME.getType())){
                log.error("user refund error --> doesn't have consuming record. user id :{}", userId);
                throw new RuntimeException("no consuming records, can't refund!");
            }
        按理说 退款金额一般与订单金额一致，或通过某些扣款公式（如扣除运费等）后需要保持一致；
        但所给题目并为提及，故省略
        if (!Objects.equals(record.getAmount(), amount)) {
                log.error("wrong refund money amount! userId :{}\t refundId:{}", userId, recordId);
                throw new RuntimeException("refund money must be same as the costs");
            }*/

    @Transactional(rollbackFor = Exception.class)
    @Override
    public String refundMoney(/*Long recordId, */Long userId, Integer amount) {
        if (isIdNotValidate(userId) /*|| isIdNotValidate(recordId)*/) {
            throw new RuntimeException("id not validate!");
        }
        if (amount < 0) {
            throw new RuntimeException("invalidate amount");
        }
        RLock lock = redissonClient.getLock(userId.toString());
        try {
            if (lock.tryLock()) {
                UserWallet userWallet = userWalletMapper.selectById(userId);
                if (userWallet == null) {
                    throw new RuntimeException("user not exists");
                }
                Integer balance = userWallet.getBalance();
                //更新金额
                userWallet.setBalance(balance + amount);
                TransactRecord transactRecord = createTransactRecord(userId, amount,
                        TransactTypeEnum.REFUND.getType());
                //保存记录
                transactRecordService.save(transactRecord);
                userWalletMapper.updateById(userWallet);
                return String.format("user-%s successfully refund %d CNY", userWallet.getUsername(), amount);
            }
        } finally {
            lock.unlock();
        }
        return "try again later";
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String topUp(Long userId, Integer amount) {
        if (isIdNotValidate(userId)) {
            throw new RuntimeException("user id not validate!");
        }
        if (amount <= 0) {
            throw new RuntimeException("top up amount should more than 0 !");
        }
        RLock lock = redissonClient.getLock(userId.toString());
        try {
            if (lock.tryLock()) {
                UserWallet userWallet = userWalletMapper.selectById(userId);
                if (userWallet == null) {
                    throw new RuntimeException("user not exists");
                }
                Integer balance = userWallet.getBalance();
                //更新金额
                userWallet.setBalance(balance + amount);
                TransactRecord transactRecord = createTransactRecord(userId, amount,
                        TransactTypeEnum.REFUND.getType());
                //保存记录
                transactRecordService.save(transactRecord);
                userWalletMapper.updateById(userWallet);
                return String.format("user-%s successfully top up %d.",
                        userWallet.getUsername(), amount);
            }
        } finally {
            lock.unlock();
        }
        return "try again later";
    }

    //增加用户余额的方法




    //创建消费记录
    private TransactRecord createTransactRecord(Long userId, Integer amount, int type) {
        TransactRecord transactRecord = new TransactRecord();
        transactRecord.setUserId(userId);
        transactRecord.setTransactType(type);
        transactRecord.setAmount(amount);
        return transactRecord;
    }


    /**
     * 模拟远程调用银行接口
     */
    private boolean bankRemoteCall(Long userId, Integer amount) {
        return true;
    }
}




